Objetivo e contexto:

O time de aprendizagem da Universidade quer entender qual o perfil das pessoas estudantes, quais são os fatores relacionado ao desempenho das pessoas e quais recomendações/iniciativas você sugere para que o desempenho das pessoas estudantes melhore.

O trabalho foi explorar os dados dispobilizados e direcionar a tomada de decisão do negócio.

Base de dados: OULAD - Open University Learning Analytics Dataset

Descrição:

Um conjunto de dados contendo informações demográficas sobre os alunos, seus cursos frequentados e os resultados finais de cada curso.

Roteiro

  1. Mais sobre o conjunto de dados;
  2. Esquema do banco de dados;
  3. Preparando o ambiente para análise dos dados;
  4. Entendendo o pefil das pessoas estudantes(I);
  5. Fatores de desempenho(II);
  6. Conclusão(III).

1. Mais sobre o conjunto de dados

O conjunto de dados anônimos da Open University Learning Analytics Dataset (OULAD), contém dados sobre cursos, alunos e suas interações com o Virtual Learning Environment (VLE) para sete cursos selecionados. As apresentações dos cursos começão em fevereiro e outubro - são marcadas como “B” e “J” respectivamente. O conjunto de dados consiste em tabelas conectadas usando identificadores exclusivos. Todas as tabelas são armazenadas no formato csv.

Kuzilek J., Hlosta M., Zdrahal Z. Open University Learning Analytics dataset Sci.

2. Esquema do Banco de dados

Aqui temos um esquema (https://analyse.kmi.open.ac.uk/open_dataset) para ilustrar a estrutura de dados do conjunto.

Como você pode ver, existem muitos tipos diferentes de dados envolvidos, mas como queremos entender o perfil das pessoas estudantes e os fatores de desempenho utilizaremos:

Indo para o site indicado, podemos ver que estas informações estam contidas nas seguintes tabelas:

Essas tabelas serão nossas fontes de dados para cumprimento dos objetivos.

3.Preparanndo o ambiente para a análise de dados

Para realizar essa análise contaremos com o uso de dois pacotes do R, o dplyr e o plotly. Um para auxiliar na manipulação das tabelas e o outro na geração dos gráficos que serão apresentados.

Os pacotes estão disponíveis em:

Ou utilizando os comandos:

# install.packages("dplyr")
# install.packages("plotly")

Vamos utilizar o libary para chamar os pacotes após a instalação:

library(dplyr)
library(plotly)

Para finalizarmos a preparação do ambiente devemos informar o local onde se encontra os dados de entrada (inputs):

# setwd(dir = ".../Input")

4. Entendendo o perfil das pessoas estudantes(I)

Para podermos entender o perfil dos alunos presentes na base de dados utilizaremos a tabela studentInfo pois a mesma contém informações demográficas sobre os alunos junto com seus resultados. O arquivo contém as seguintes colunas:

student_info <-
  read.csv(
    file = paste0(getwd(),"/Input/studentInfo.csv")
  )

colnames(student_info)
##  [1] "code_module"          "code_presentation"    "id_student"          
##  [4] "gender"               "region"               "highest_education"   
##  [7] "imd_band"             "age_band"             "num_of_prev_attempts"
## [10] "studied_credits"      "disability"           "final_result"

Utilizaremos apenas as colunas que contém informações ligadas ao perfil demográfico do aluno, deixaremos também apenas os valores sem repetição, para termos um dado único de cada aluno:

student_info_profile <-
  student_info[,c(2:6,8,11)] %>%
  distinct(id_student, .keep_all = TRUE)

colnames(student_info_profile)
## [1] "code_presentation" "id_student"        "gender"           
## [4] "region"            "highest_education" "age_band"         
## [7] "disability"

Gerando os quantitativos:

#Tipos de apresentação e a quantidade de alunos
student_info_presentation <-
  student_info_profile %>%
  mutate(
    type_presentation = substr(code_presentation, nchar(code_presentation), nchar(code_presentation))
  ) %>%
  group_by(type_presentation) %>%
  count() %>%
  ungroup()

student_info_presentation
## # A tibble: 2 x 2
##   type_presentation     n
##   <chr>             <int>
## 1 B                 11190
## 2 J                 17595
#Alunos por gênero
student_info_gender <-
  student_info_profile %>%
  group_by(gender) %>%
  count() %>%
  ungroup()

student_info_gender
## # A tibble: 2 x 2
##   gender     n
##   <chr>  <int>
## 1 F      13739
## 2 M      15046
#Alunos por regiâo
student_info_region <-
  student_info_profile %>%
  group_by(region) %>%
  count() %>%
  ungroup()

student_info_region
## # A tibble: 13 x 2
##    region                   n
##    <chr>                <int>
##  1 East Anglian Region   3000
##  2 East Midlands Region  2095
##  3 Ireland               1072
##  4 London Region         2845
##  5 North Region          1588
##  6 North Western Region  2548
##  7 Scotland              2934
##  8 South East Region     1875
##  9 South Region          2737
## 10 South West Region     2154
## 11 Wales                 1876
## 12 West Midlands Region  2269
## 13 Yorkshire Region      1792
#Escolaridade
student_info_education <-
  student_info_profile %>%
  group_by(highest_education) %>%
  count() %>%
  ungroup()

student_info_education
## # A tibble: 5 x 2
##   highest_education               n
##   <chr>                       <int>
## 1 A Level or Equivalent       12355
## 2 HE Qualification             4092
## 3 Lower Than A Level          11780
## 4 No Formal quals               306
## 5 Post Graduate Qualification   252
#Idade
student_info_age <-
  student_info_profile %>%
  group_by(age_band) %>%
  count() %>%
  ungroup()

student_info_age
## # A tibble: 3 x 2
##   age_band     n
##   <chr>    <int>
## 1 0-35     20145
## 2 35-55     8462
## 3 55<=       178
#Portador de deficiência
student_info_disability <-
  student_info_profile %>%
  group_by(disability) %>%
  count() %>%
  ungroup()

student_info_disability
## # A tibble: 2 x 2
##   disability     n
##   <chr>      <int>
## 1 N          26068
## 2 Y           2717
#Informações demográficas compiladas
student_demographic_data <-
  student_info_profile %>%
  group_by(gender, region, highest_education, age_band, disability) %>%
  count() %>%
  ungroup()

student_demographic_data
## # A tibble: 336 x 6
##    gender region              highest_education     age_band disability     n
##    <chr>  <chr>               <chr>                 <chr>    <chr>      <int>
##  1 F      East Anglian Region A Level or Equivalent 0-35     N            392
##  2 F      East Anglian Region A Level or Equivalent 0-35     Y             70
##  3 F      East Anglian Region A Level or Equivalent 35-55    N            169
##  4 F      East Anglian Region A Level or Equivalent 35-55    Y             29
##  5 F      East Anglian Region HE Qualification      0-35     N             65
##  6 F      East Anglian Region HE Qualification      35-55    N             66
##  7 F      East Anglian Region Lower Than A Level    0-35     N            399
##  8 F      East Anglian Region Lower Than A Level    0-35     Y             60
##  9 F      East Anglian Region Lower Than A Level    35-55    N            200
## 10 F      East Anglian Region Lower Than A Level    35-55    Y             41
## # ... with 326 more rows

Observando os gráficos chegamos a conclusão que temos um perfil multicultural de alunos, vindos de diversas regiões, com diversos níveis de conhecimento diferentes, com a maioria tendo até 35 anos de idade. Porém decidimos correlacioanar os dados que julgamos mais importantes(idade, escolaridade e região) para entender melhor o perfil da amostra:

#Escolaridade por região dos alunos
student_info_region_education <-
  student_info_profile %>%
  group_by(region, highest_education) %>%
  count() %>%
  ungroup()

student_info_region_education
## # A tibble: 60 x 3
##    region               highest_education               n
##    <chr>                <chr>                       <int>
##  1 East Anglian Region  A Level or Equivalent        1305
##  2 East Anglian Region  HE Qualification              313
##  3 East Anglian Region  Lower Than A Level           1324
##  4 East Anglian Region  No Formal quals                49
##  5 East Anglian Region  Post Graduate Qualification     9
##  6 East Midlands Region A Level or Equivalent         944
##  7 East Midlands Region HE Qualification              176
##  8 East Midlands Region Lower Than A Level            960
##  9 East Midlands Region No Formal quals                11
## 10 East Midlands Region Post Graduate Qualification     4
## # ... with 50 more rows

Qundo correlacionamos a escolaridade por região, vemos uma amsotra ainda homogênea.

#Escolaridade por idade dos alunos
student_info_age_education <-
  student_info_profile %>%
  group_by(age_band, highest_education) %>%
  count() %>%
  ungroup()

student_info_age_education
## # A tibble: 14 x 3
##    age_band highest_education               n
##    <chr>    <chr>                       <int>
##  1 0-35     A Level or Equivalent        9290
##  2 0-35     HE Qualification             2228
##  3 0-35     Lower Than A Level           8284
##  4 0-35     No Formal quals               258
##  5 0-35     Post Graduate Qualification    85
##  6 35-55    A Level or Equivalent        3032
##  7 35-55    HE Qualification             1756
##  8 35-55    Lower Than A Level           3469
##  9 35-55    No Formal quals                48
## 10 35-55    Post Graduate Qualification   157
## 11 55<=     A Level or Equivalent          33
## 12 55<=     HE Qualification              108
## 13 55<=     Lower Than A Level             27
## 14 55<=     Post Graduate Qualification    10

Agora, correlacionando Idade x Escolaridade, conseguimos indentificar pontos onde existe uma maior quantidade de amostras, assim decidimos criar uma tabela apenas dos estudantes que contém esse perfil:

#Escolaridade por idade dos alunos
representative_student_group_info <-
  student_info_profile %>%
  subset(
    age_band == "0-35" & 
      (
        highest_education == "A Level or Equivalent"  | 
          highest_education == "Lower Than A Level"  | 
          highest_education == "HE Qualification"
      )
    )

#O quanto esse grupo representa
group_percentage <- 
  (nrow(representative_student_group_info)/nrow(student_info_profile))*100

group_percentage
## [1] 68.79277

Após essas correlações, observar-se que as pessoas estudantes de idade ‘0-35’ que possuem Nível superior, ou Ensino médio completo/cursando, representam o perfil geral dos alunos, por serem 68.8% das amostras..

5. Fatores de dsempenho(II)

O desempenho em cada avaliação é um bom indicador do conhecimento dos alunos sobre o curso. Iremos separar os exames finais das restantes avaliações, dado que o seu estatuto e a participação na avaliação final são diferentes das restantes.

Leitura dos dados de avaliação

  #Informações das provas por estudante
student_assessment <-
  read.csv(
    file = paste0(getwd(),"/Input/studentAssessment.csv")
  ) 

  #Informações das provas
assessments <-
  read.csv(
    file = paste0(getwd(),"/Input/assessments.csv")
  )

Separando os Exames

final_exams <-
  assessments %>%
  subset(assessment_type == "Exam")

head(final_exams)
##    code_module code_presentation id_assessment assessment_type date weight
## 6          AAA             2013J          1757            Exam   NA    100
## 12         AAA             2014J          1763            Exam   NA    100
## 24         BBB             2013B         14990            Exam   NA    100
## 36         BBB             2013J         15002            Exam   NA    100
## 48         BBB             2014B         15014            Exam   NA    100
## 54         BBB             2014J         15025            Exam   NA    100
others_exams <-
  assessments %>%
  subset(assessment_type != "Exam")

head(others_exams)
##   code_module code_presentation id_assessment assessment_type date weight
## 1         AAA             2013J          1752             TMA   19     10
## 2         AAA             2013J          1753             TMA   54     20
## 3         AAA             2013J          1754             TMA  117     20
## 4         AAA             2013J          1755             TMA  166     20
## 5         AAA             2013J          1756             TMA  215     30
## 7         AAA             2014J          1758             TMA   19     10

Vamos indentificar qual a média de avaliação por aluno por modulo, e indentificar as atividade de quem tem as maiores e menores médias de avaliação.

#Criando data frame 'student_group_kpis'

student_group_kpis <-
  student_assessment %>%
  mutate(pass = ifelse(score>=40, TRUE, FALSE))

#Juntando com as informações de exame e criando as colunas de quem passou no exame e o peso da grade

student_group_others_exams <-
  student_group_kpis %>%
  inner_join(others_exams, by = "id_assessment") 

student_group_others_exams <-
  student_group_others_exams %>%
  mutate(weight_grade = score*weight/100)

head(student_group_others_exams[,c(1,6,7,11)])
##   id_assessment pass code_module weight
## 1          1752 TRUE         AAA     10
## 2          1752 TRUE         AAA     10
## 3          1752 TRUE         AAA     10
## 4          1752 TRUE         AAA     10
## 5          1752 TRUE         AAA     10
## 6          1752 TRUE         AAA     10
#Média de avaliação final por aluno por módulo

avg_grade_others_exams <-
  student_group_others_exams %>%
  dplyr::group_by(id_student, code_module, code_presentation) %>% 
  mutate(avg_grade = sum(weight_grade)) %>%
  select("id_student","code_module","code_presentation", "avg_grade")

head(avg_grade_others_exams)
## # A tibble: 6 x 4
## # Groups:   id_student, code_module, code_presentation [6]
##   id_student code_module code_presentation avg_grade
##        <int> <chr>       <chr>                 <dbl>
## 1      11391 AAA         2013J                  82.4
## 2      28400 AAA         2013J                  65.4
## 3      31604 AAA         2013J                  76.3
## 4      32885 AAA         2013J                  55  
## 5      38053 AAA         2013J                  66.9
## 6      45462 AAA         2013J                  67.8
#Pontuação dos exames finais

student_group_final_exams <-
  student_group_kpis %>%
  inner_join(final_exams, by = "id_assessment")  %>%
  dplyr::rename("exams_score" = "score")%>%
  select("id_student","code_module","code_presentation", "exams_score")

head(student_group_final_exams)
##   id_student code_module code_presentation exams_score
## 1     558914         CCC             2014B          32
## 2     559706         CCC             2014B          78
## 3     559770         CCC             2014B          54
## 4     560114         CCC             2014B          64
## 5     560311         CCC             2014B         100
## 6     560494         CCC             2014B          92

Tendo levantado os dados das avaliações, vamos verificar os dados de interações do aluno com o ambiente virtual da universidade

Verificando interações:

Os conjuntos de dados referentes ao ambiente virtual da universidade contêm o feed de interação dos alunos com o conteúdo disponível. A partir desses dados, podemos inferir como um aluno estava em contato com seus assuntos, se o estudou de forma sólida e como utilizou o conteúdo.

#Lendo as tabelas de interações

student_vle <-
  read.csv(
    file = paste0(getwd(),"/Input/studentVle.csv")
  )

head(student_vle)
##   code_module code_presentation id_student id_site date sum_click
## 1         AAA             2013J      28400  546652  -10         4
## 2         AAA             2013J      28400  546652  -10         1
## 3         AAA             2013J      28400  546652  -10         1
## 4         AAA             2013J      28400  546614  -10        11
## 5         AAA             2013J      28400  546714  -10         1
## 6         AAA             2013J      28400  546652  -10         8
vle <-
  read.csv(
    file = paste0(getwd(),"/Input/vle.csv")
  )

head(vle)
##   id_site code_module code_presentation activity_type week_from week_to
## 1  546943         AAA             2013J      resource        NA      NA
## 2  546712         AAA             2013J     oucontent        NA      NA
## 3  546998         AAA             2013J      resource        NA      NA
## 4  546888         AAA             2013J           url        NA      NA
## 5  547035         AAA             2013J      resource        NA      NA
## 6  546614         AAA             2013J      homepage        NA      NA

Se observarmos a tabela VLE, podemos indentificar que existem alguns dados sem referência de périodo de uso, portanto, para tornar a análise mais viável iremos filtra-los.

#limpando os dados de VLE, pois algumas amostras não possuem a semana de referência para os materiais

vle <-
  vle %>%
  subset(!is.na(week_from))

head(vle)
##     id_site code_module code_presentation activity_type week_from week_to
## 114  546732         AAA             2013J     oucontent         2       2
## 199  546719         AAA             2013J     oucontent         1       1
## 211  546681         AAA             2013J     oucontent         1       1
## 265  877040         AAA             2014J     oucontent         2       2
## 324  877045         AAA             2014J     oucontent         1       1
## 392  877044         AAA             2014J     oucontent         1       1

Aqui podemos acompanhar o tempo médio após o início do curso que o aluno fez para utilizar os materiais e a quantidade média de cliques por material:

#Média geral por aluno por módulo

avg_per_student <-
  student_vle %>%
  dplyr::group_by(id_student, code_module, code_presentation) %>%
  mutate(
    date_mean = mean(date),
    sum_click_mean = mean(sum_click)) %>%
  select("id_student","code_module","code_presentation", "date_mean", "sum_click_mean")

head(avg_per_student)
## # A tibble: 6 x 5
## # Groups:   id_student, code_module, code_presentation [1]
##   id_student code_module code_presentation date_mean sum_click_mean
##        <int> <chr>       <chr>                 <dbl>          <dbl>
## 1      28400 AAA         2013J                  87.0           3.34
## 2      28400 AAA         2013J                  87.0           3.34
## 3      28400 AAA         2013J                  87.0           3.34
## 4      28400 AAA         2013J                  87.0           3.34
## 5      28400 AAA         2013J                  87.0           3.34
## 6      28400 AAA         2013J                  87.0           3.34

Como não podemos indentificar fatores de desempenho nos estudantes que desitiram, iremos tirar eles das amostras representativas:

#Filtrando somente amostras representativas (Segundo a analise de perfil dos estudantes)

representative_student_group_info <-
  student_info %>%
  subset(
    age_band == "0-35" & 
      (
        highest_education == "A Level or Equivalent"  | 
          highest_education == "Lower Than A Level"  | 
          highest_education == "HE Qualification"
      ) &
      final_result != "Withdrawn"
  ) %>%
  distinct(id_student, .keep_all = TRUE)
#Compilando as tabelas relevantes

df_1 <-
  inner_join(avg_grade_others_exams, student_group_final_exams, 
             by = c("id_student", "code_module", "code_presentation"))

df_2 <-
  inner_join(representative_student_group_info, df_1, 
             by = c("id_student", "code_module", "code_presentation"))

final_df <-
  inner_join(df_2, avg_per_student, 
             by = c("id_student", "code_module", "code_presentation")) %>%
  select(num_of_prev_attempts, final_result, avg_grade, exams_score, date_mean, sum_click_mean)

head(final_df[,-2])
##   num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 1                    0     89.65          94  119.3379       4.343939
## 2                    0     89.65          94  119.3379       4.343939
## 3                    0     89.65          94  119.3379       4.343939
## 4                    0     89.65          94  119.3379       4.343939
## 5                    0     89.65          94  119.3379       4.343939
## 6                    0     89.65          94  119.3379       4.343939
summary(final_df[,-2])
##  num_of_prev_attempts   avg_grade      exams_score       date_mean     
##  Min.   :0.0000       Min.   : 3.72   Min.   :  0.00   Min.   : 27.01  
##  1st Qu.:0.0000       1st Qu.:58.88   1st Qu.: 51.00   1st Qu.: 89.75  
##  Median :0.0000       Median :74.25   Median : 67.00   Median :103.28  
##  Mean   :0.1059       Mean   :70.98   Mean   : 65.98   Mean   :103.55  
##  3rd Qu.:0.0000       3rd Qu.:86.15   3rd Qu.: 82.00   3rd Qu.:115.64  
##  Max.   :5.0000       Max.   :99.80   Max.   :100.00   Max.   :230.00  
##                       NA's   :20653                                    
##  sum_click_mean  
##  Min.   : 1.077  
##  1st Qu.: 2.273  
##  Median : 2.681  
##  Mean   : 2.956  
##  3rd Qu.: 3.331  
##  Max.   :16.242  
## 
nrow(final_df[final_df$final_result == "Pass",])
## [1] 8043978
nrow(final_df[final_df$final_result == "Distinction",])
## [1] 2145211
nrow(final_df[final_df$final_result == "Fail",])
## [1] 1282021

Com uma contagem de “Pass” muito maior do que os outros rótulos, devemos ficar atentos. Foram detectados dois outliers: Um com média de cliques bem acima dos valores padrões e outro com uma única ocorrência de um número de tentativas anteriores. Para manter nossos dados o mais consistentes possível, esses casos serão removidos.

final_df <-
  final_df %>%
  subset(sum_click_mean<10)
final_df <-
  final_df %>%
  subset(num_of_prev_attempts<4)
nrow(final_df)
## [1] 11435514

Separando os dados para enteder o perfil dos estudantes que passaram e os que falharam

pass_student <-
  final_df %>%
  subset(final_result != "Fail")

head(pass_student[,-2])
##   num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 1                    0     89.65          94  119.3379       4.343939
## 2                    0     89.65          94  119.3379       4.343939
## 3                    0     89.65          94  119.3379       4.343939
## 4                    0     89.65          94  119.3379       4.343939
## 5                    0     89.65          94  119.3379       4.343939
## 6                    0     89.65          94  119.3379       4.343939
nrow(pass_student)
## [1] 10155306
fail_student <-
  final_df %>%
  subset(final_result == "Fail")

head(fail_student[,-2])
##       num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 42369                    0     40.96          24  96.93333       5.114286
## 42370                    0     40.96          24  96.93333       5.114286
## 42371                    0     40.96          24  96.93333       5.114286
## 42372                    0     40.96          24  96.93333       5.114286
## 42373                    0     40.96          24  96.93333       5.114286
## 42374                    0     40.96          24  96.93333       5.114286
nrow(fail_student)
## [1] 1280208

Como temos uma grande quantidade de amostras, utilizarei abaixo apenas as primeiras cem mil amostras (100.000) para montar os gráficos:

Dados dos que passaram

Dados dos que falharam

6. Conclusão(III).

Após a análise realizada, pode-se observar que os alunos que passaram e tiveram um melhor desempenho, no geral tinham mais tempo de interação com o material disponibilizado, além de notas mais elevadas nos exames de Tutor Marked Assessment (TMA) e de Computer Marked Assessment (CMA) em relação aos que não passaram. Isso pode indicar que, focar em iniciativas para aumentar a interatividade do estudante com a plataforma online, aumentando o engajamento com as provas que não são do exame final, é provável que a chance de sucesso dos alunos que não passaram aumentaria. Para que essa análise fique mais assertiva seria necessário seguir com algum modelo de regressão e identificar se esta hipótese é válida.